"""
Copyright 2012 Wieser Dietmar, Margit Mutschlechner.

This file is part of Multimedia Social Networking Platform.

Multimedia Social Networking Platform is free software: 
you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Multimedia Social Networking Platform is distributed
in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Multimedia Social Networking Platform.  
If not, see <http://www.gnu.org/licenses/>.

"""

from threading import Thread
from msnp.service.network import NetworkLock, NetworkController, CustodyQueue, \
    BackupController, networktimer, LeaveRequest
from msnp.service.network.sending import Sender
from msnp.logger import Logger
from msnp.model import PTPModel
import traceback
from msnp.persistence import PersistenceController
from msnp.model.PTPModel import BackupContent
from msnp.service.network.logic import MessageExchangeLogic, ResponsibilityLogic
from msnp.service.network.model.CustodyModel import CustodyQueueType
from msnp.service.network.model.ReliableTransportModel import RTJoinMessage
from msnp.service.network.sending import ReliableNetworkExchange

class SuperPeerAbilityThread(Thread):
    """Thread that performs the upgrade of a low level peer at the responsible super peer.
    
    Attributes:
        __lowLevelPeer: The PeerInfo instance.
    """
    
    def __init__(self, lowLevelPeer):
        """the init method"""
        Thread.__init__(self)
        self.__lowLevelPeer = lowLevelPeer
        
    def run(self):
        """This method performs the upgrade of a low level peer at the responsible super peer.
    
        The whole function is locked so that only one peer at a time can upgrade.
        The current peer checks whether it is still the responsible peer for low level peer.
        If not it starts a new responsibility search.
        If si it shifts all corresponding data to the low level peer (all data the low level peer is responsible for).
        At the same time this is the test whether the low level peer is reachable from outside.
        If the low level peer gets all data a function is called so that he can join the ring and upgrade.
        If that was successful the super peer deletes all data he shifted to the new peer and the upgrade is finished successful.
        """
        NetworkLock.acquireJoinLock()
        NetworkLock.acquireBackupLock()
        LeaveRequest.block()
        try:
            llPeerName = self.__lowLevelPeer.getName()
            if ResponsibilityLogic.amIResponsibleIdentityFor(self.__lowLevelPeer.getHashedNameLong()) == False:
                #if peer is shifted do nothing -> when he calls the next time a keepalive he gets the shift-message
                #otherwise if I am not responsible I am the entry peer and search for the responsible peer
                if CustodyQueue.containsShiftMessageFor(llPeerName) == False:
                    Logger.info("not responsible to upgrade peer " + llPeerName + ", re-hello him")
                    MessageExchangeLogic.executeHello(self.__lowLevelPeer, True)
            else:
                #lock so that no low level peer can retrieve messages during shifting process
                NetworkLock.acquireKeepAliveLock()
                try:
                    #send shifted peers
                    Logger.info("Peer " + llPeerName + " wants to upgrade")
                    (llPeerList, messages) = NetworkController.getMySelf().getLowLevelPeersAndMessagesForName(llPeerName)
                    returnValue = Sender.sendShiftedData(self.__lowLevelPeer, llPeerList, messages)
                    if returnValue == PTPModel.ReturnType.SUCCESSFUL:
                        #send shifted backupcontent
                        backupContent = BackupController.selectBackupToShiftForUsername(self.__lowLevelPeer)
                        if backupContent != None:
                            returnValue = Sender.sendBackup(self.__lowLevelPeer, backupContent, False)
                            if returnValue == PTPModel.ReturnType.SUCCESSFUL:
                                #store old predecessor to reset if an error occurred at the joining peer
                                oldPredecessor = NetworkController.getMySelf().getPredecessor()
                                NetworkController.getMySelf().setPredecessor(self.__lowLevelPeer)
                                returnValue = Sender.joinRing(self.__lowLevelPeer, oldPredecessor)
                                if returnValue == PTPModel.ReturnType.SUCCESSFUL:
                                    #store shift-messages and delete shifted low level peers
                                    for llPeer in llPeerList:
                                        llPeerInListName = llPeer.getIdentity().getName()
                                        CustodyQueue.deleteAllMessagesForName(llPeerInListName)
                                        CustodyQueue.addShiftedPeerMessage(llPeerInListName, self.__lowLevelPeer)
                                    NetworkController.getMySelf().removeLowLevelPeers(llPeerList)
                                    NetworkController.getMySelf().removeLowLevelPeer(llPeerName)
                                    #set shifted backup as backup in own db
                                    backupContent.setType(PTPModel.BackupType.UPDATE)
                                    backupContent.setFromUsername(llPeerName)
                                    #processBackup doesn't update my fingers because fromUsername is not None
                                    BackupController.processBackup(backupContent, True)
                                    #remove backup at old predecessor and shift to new predecessor
                                    BackupController.insertAtFinger(self.__lowLevelPeer)
                                    BackupController.deleteFromFinger(oldPredecessor)
                                    #remove shifted backup from fingers
                                    myName = NetworkController.getMySelf().getMyIdentity().getName()
                                    backupContent.setFromUsername(myName)
                                    backupContent.setType(PTPModel.BackupType.DELETE_SELECTED_BACKUPFROM)
                                    #SENDBACKUP: backupContent has to be computed here
                                    BackupController.updateBackupAtFingers(backupContent)
                                    
                                    #set successor if it is None -> only 2 peers in the ring -> predecessor is successor
                                    #otherwise problems with other peers that want to upgrade
                                    if NetworkController.getMySelf().getSuccessor() == None:
                                        NetworkController.getMySelf().setSuccessor(self.__lowLevelPeer)
                                    
                                    #send messages I received after joining peer called this method
                                    queueMessages = CustodyQueue.retrieveAndDelete(llPeerName, CustodyQueueType.ALL_TYPES)
                                    if queueMessages != None and len(queueMessages) > 0:
                                        Logger.info("send " + str(len(queueMessages)) + " messages to upgrading peer")
                                        Sender.sendMessages(self.__lowLevelPeer, queueMessages)
                                    else:
                                        Logger.info("no messages to send to upgrading peer")
                                    Logger.info("peer " + llPeerName + " is upgraded to superpeer")
                                else:
                                    NetworkController.getMySelf().setPredecessor(oldPredecessor)
                                    Logger.info("join of peer " + llPeerName + " not successful in superPeerAbilityThread")
                            else:
                                Logger.info("sending backup content failed")
                        else:
                            Logger.info("backupcontent is None, should not happen!!!")
                    else:
                        Logger.info("shifting of low level peers not successful in superPeerAbilityThread (sendShiftedData)")
                finally:
                    NetworkLock.releaseKeepAliveLock()
        finally:
            NetworkLock.releaseJoinLock()
            NetworkLock.releaseBackupLock()
            LeaveRequest.release()
            
            
class ProcessMessages(Thread):
    """Thread that processes the messages the responsible super peer sent to the current peer after the upgrade process.

    Attributes:
        __queueMessages: The list of CustodyMessage instances.
    """
    
    def __init__(self, queueMessages):
        """the init method"""
        Thread.__init__(self)
        self.__queueMessages = queueMessages
        
    def run(self):
        """This method processes the messages the responsible super peer sent to the current peer after the upgrade process.
        """
        
        try:
            if self.__queueMessages != None:
                Logger.info("process messages my super peer got after I called upgrade-function")
                networktimer.KeepAliveTimer.processReceivedMessages(self.__queueMessages)
        except:
            Logger.info("ERROR while processing messages my super peer got after I called upgrade-function")
            Logger.info(traceback.format_exc())


def join(successor, predecessor):
    """Joining the ring during the upgrade process after being logged in as low level peer.
    
    This function is called from the responsible super peer during the upgrade process.
    It sets the new successor and predecessor, sends a backup to the new predecessor, initiates a RTJoinMessage and starts the LowLevelPeerCrashTimer.
    
    Args:
        successor: The successor (PeerInfo instance)
        predecessor: The predecessor (PeerInfo instance)
        
    Returns:
        The return is defined by ReturnType and be one of the following values: SUCCESSFUL or ERROR
    """
    
    NetworkLock.acquireFingerLock()
    NetworkLock.acquireBackupLock()
    LeaveRequest.block()
    try:
        myself = NetworkController.getMySelf()
        if successor != None:
            successorOfPredecessor = Sender.getSuccessor(predecessor)
            #the successor of the given predecessor must be the same as the given successor.
            #otherwise the ring could lost consistency
            if successorOfPredecessor == None or successorOfPredecessor.getName() == successor.getName():
                #set own successor
                myself.setSuccessor(successor)
                Logger.info('successor is ' + successor.getNameBundle())
                #set own predecessor
                if predecessor != None:
                    myself.setPredecessor(predecessor)
                else:
                    #if the responsible peer is the only peer in the ring he returned None for predecessor
                    myself.setPredecessor(successor)
                predecessor = myself.getPredecessor()
                Logger.info('predecessor is ' + predecessor.getNameBundle())
                #update fingertables of other peers
                myIdentity = myself.getMyIdentity()
                message = RTJoinMessage(myIdentity)
                ReliableNetworkExchange.initiateRTMessage(message)
                #if i joined the ring i am no more a low level peer
                myself.setIsLowLevelPeer(False)
                myself.setResponsibleSuperPeer(myself.getMyIdentity())
                networktimer.LowLevelPeerCrashTimer.startLowLevelPeerCrashTimer()
                #insert at predecessor because we do not receive a callback from it
                BackupController.insertAtFinger(predecessor)
                Logger.info("after insertAtFinger")
                return PTPModel.ReturnType.SUCCESSFUL
            else:
                Logger.info("cannot join the ring because the successor of the predecessor is != my successor")
                __joinFailed()
                return PTPModel.ReturnType.ERROR
        else:
            Logger.info("successor None in join")
    except:
        Logger.info('error in networkcontroller.join')
        Logger.info(traceback.format_exc())
        __joinFailed()
        return PTPModel.ReturnType.ERROR
    finally:
        NetworkLock.releaseFingerLock()
        NetworkLock.releaseBackupLock()
        LeaveRequest.release()
        
        
def __joinFailed():
    """This function is executed if an unexpected behavior occurred during the execution of the join function.
    
    The successor and predecessor are set to None, the LowLevelPeerCrashTimer is stopped, the KeepAliveTimer is started and the database
    and the CustodyQueue are cleaned.
    """
    
    myself = NetworkController.getMySelf()
    #I have perhaps some shifted low levels and some of the backup already processed and so on -> undo everything if an error occurred
    myself.setSuccessor(None)
    myself.setPredecessor(None)
    myself.setIsLowLevelPeer(True)
#    myself.setResponsibleSuperPeer(None)
    networktimer.LowLevelPeerCrashTimer.stopLowLevelPeerCrashTimer()
    networktimer.KeepAliveTimer.startKeepAliveTimer()
    PersistenceController.cleanDatabase()
    myself.removeAllLowLevelPeers()
    CustodyQueue.deleteAllMessages()
        
        
def leaveNetwork():
    """This method executes the network leave.
    
    The successor of the current peer changes its predecessor to the predecessor of the current peer.
    All backups at the fingers are deleted.
    
    Returns:
        True if the leave was successful, False otherwise.
    """
    
    myself = NetworkController.getMySelf()
    if myself.isLowLevelPeer() == False:
        #if I leave the network fingers of other peers have to point to my successor instead of pointing to me
        predecessor = myself.getPredecessor()
        successor = myself.getSuccessor()
        myIdentity = myself.getMyIdentity()
        #set new predecessor of successor because i leave the network
        returntype = Sender.updatePredecessorOfSuccessor(predecessor, successor)
        ret = True
        if returntype == PTPModel.ReturnType.SUCCESSFUL:
            backup = BackupContent(myIdentity.getName(), None, None, None, BackupController.BackupType.DELETE_ALL)
            BackupController.updateBackupAtFingersWithoutPredecessor(backup)
        else:
            Logger.info("normal leave (updatepredecessorofsuccessor) failed so it is a crash")
            ret = False
        return ret
    else:
        return Sender.logoutLowLevelPeer()
        
        
def executeSuperPeerAbilityTest(lowLevelPeer):
    """This function executes the thread that performs the upgrade of a low level peer at the responsible super peer.
    
    Args:
        lowLevelPeer: The PeerInfo instance.
    """
    
    testThread = SuperPeerAbilityThread(lowLevelPeer)
    testThread.start()
    
    
def executeSendShiftedData(llPeerList, messages):
    """Function that processes the messages the responsible super peer sent to the current peer during the upgrade process.
    
    Args:
        llPeerList: A list of low level peers (PeerInfo instances).
        messages: a list of CustodyMessage instances.
        
    Returns:
        The return is defined by ReturnType and be one of the following values: SUCCESSFUL or ERROR
    """
    
    try:
        NetworkController.getMySelf().addLowLevelPeers(llPeerList)
        CustodyQueue.addCustodyMessages(messages)
        return PTPModel.ReturnType.SUCCESSFUL
    except:
        #I have perhaps some shifted low levels already processed-> remove everything if an error occurred
        NetworkController.getMySelf().removeAllLowLevelPeers()
        CustodyQueue.deleteAllMessages()
        return PTPModel.ReturnType.ERROR


def executeJoinRing(newSuccessor, newPredecessor):
    """This call is forwarded to the join function.
    
    Args:
        newSuccessor: the successor (PeerInfo instance)
        newPredecessor: the predecessor (PeerInfo instance)
        
    Returns:
        The return is defined by ReturnType and be one of the following values: SUCCESSFUL or ERROR
    """
    return join(newSuccessor, newPredecessor)


def executeLogoutLowLevelPeer(llPeerName): 
    """This function if responsible for the logout of a low level peer at the responsible super peer.
    
    The low level peer is deleted from the list of online low level peers.
    All custody messages stored for this low level peer are also deleted.
    
    Args:
        llPeerName: the name of the leaving low level peer (string)
        
    Returns:
        False
    """
    
    NetworkController.getMySelf().removeLowLevelPeer(llPeerName)
    CustodyQueue.retrieveAndDelete(llPeerName, CustodyQueueType.ALL_TYPES)
    return False
    
    
def executeGetTimestamp():
    """This call is forwarded to the getBackupTimeStamp function in the BackupController module.
        
    Returns:
        Returns the current time stamp as float.
    """
    
    return BackupController.getBackupTimeStamp()


def executeSendMessages(queueMessages):
    """This function executes a thread that processes the messages the responsible super peer sent to the current peer after the upgrade process.

    Attributes:
        queueMessages: The list of CustodyMessage instances.
    """
    
    # process messages my super peer got after I called the upgrade function
    processThread = ProcessMessages(queueMessages)
    processThread.start()
